parser grammar samParser;

options {
  language = Java;
  tokenVocab = samLexer;
  backtrack =true;
}
@header {
  package traductor;
  import java.util.Arrays;
  import java.util.HashMap;
  import java.util.LinkedList;
  
  import emulador.Instruccion;
}
@members {

  ArrayList <RecognitionException> errores = new ArrayList<RecognitionException>();
  
  public boolean listaVacia (){
      if (errores.isEmpty()){
        return true;
      }else
        return false;
  }
  
  public ArrayList getListaErrores(){return errores;}
  
  
  @Override
  public void reportError (RecognitionException e){
    System.err.println (getErrorMessage(e, getTokenNames()));
    errores.add(e);
}


  private String[] primitivas = {"id", "not", "and", "or", "inc", "dec",
  "neg", "add", "sub", "mult", "div", "mod", "lt", "le", "gt", "ge", "eq", "ne",
  "to21", "to22", "new", "free", "get", "put"
  };
  private String[] registros = {"CB", "CT", "PB", "PT", "SB", "ST", "HB", "HT", "LB", "L1", "L2", "L3", "L4", "L5", "L6", "CP"};

  private HashMap<String, Integer> labels = new HashMap<String, Integer>();
  private LinkedList<Instruccion> code = new LinkedList<Instruccion>();
  private LinkedList<String> primitiveLabels = new LinkedList<String>(Arrays.asList(primitivas));
  private LinkedList<String> registerList = new LinkedList<String>(Arrays.asList(registros));
  
  private int phase = 0;
  private int sentenceCount = 0;
  
  private void addInstruccion(Instruccion ins) {
    code.add(ins);
  }
  
  private void addLabel(String s, Integer i) {
    labels.put(s, i);
  }
  
  public LinkedList<Instruccion> getCode() {
    return code;
  }
  
  public int getLabel(String s) {
    return labels.get(s);
  }
  
  private boolean isInteger(String s) {
    try {
      Integer.parseInt(s);  
      return true;
    } catch(Exception e) {
      return false;
    }
  }
  
  public void resetStructures() {
    labels = new HashMap<String, Integer>();
    code = new LinkedList<Instruccion>();
  }
}


// -----------------------------------------------------------------------------
// Parser rules
// -----------------------------------------------------------------------------

parseLabels
  : { resetStructures(); phase = 0; }
  line* EOF
  ;
  
generateCode
  : { phase = 1; }
  line* EOF
  ;

line
  : (ID COLON { if (phase == 0) addLabel($ID.getText(), sentenceCount); })? sentence { sentenceCount++; }
  ;
  
sentence
  : LOAD sizeString = size NUMERO regString = register
  { if (phase == 1) {
    int n = Integer.parseInt(sizeString);
    int d = Integer.parseInt($NUMERO.getText());
    int r = registerList.indexOf(regString);
    addInstruccion(new Instruccion(0, n, d, r));
    } }
  | LOADA NUMERO regString = register
  { if (phase == 1) {
    int d = Integer.parseInt($NUMERO.getText());
    int r = registerList.indexOf(regString);
    addInstruccion(new Instruccion(1, 0, d, r));
    } }
  | LOADI sizeString = size
  { if (phase == 1) {
    int n = Integer.parseInt(sizeString);
    addInstruccion(new Instruccion(2, n, 0, 0));
    } }
  | LOADL NUMERO
  { if (phase == 1) {
    int d = Integer.parseInt($NUMERO.getText());
    addInstruccion(new Instruccion(3, 0, d, 0));
    } }
  | STORE sizeString = size NUMERO regString = register
  { if (phase == 1) {
    int n = Integer.parseInt(sizeString);
    int d = Integer.parseInt($NUMERO.getText());
    int r = registerList.indexOf(regString);
    addInstruccion(new Instruccion(4, n, d, r));
    } }
  | STOREI sizeString = size
  { if (phase == 1) {
    int n = Integer.parseInt(sizeString);
    addInstruccion(new Instruccion(5, n, 0, 0));
    } }
  | CALL sizeString = size ID
  { if (phase == 1) {
    int n = isInteger(sizeString) ? Integer.parseInt(sizeString) : registerList.indexOf(sizeString);
    if (primitiveLabels.contains($ID.getText()))
      addInstruccion(new Instruccion(6, n, primitiveLabels.indexOf($ID.getText()), 2));
    else
      addInstruccion(new Instruccion(6, n, labels.get($ID.getText()), 0));
    } }
  | CALLI
  { if (phase == 1) addInstruccion(new Instruccion(7, 0, 0, 0)); }
  | RETURN sizeString = size NUMERO
  { if (phase == 1) {
    int n = Integer.parseInt(sizeString);
    int d = Integer.parseInt($NUMERO.getText());
    addInstruccion(new Instruccion(8, n, d, 0));
    } }
  | PUSH NUMERO
  { if (phase == 1) {
    int d = Integer.parseInt($NUMERO.getText());
    addInstruccion(new Instruccion(9, 0, d, 0));
    } }
  | POP sizeString = size NUMERO
  { if (phase == 1) {
    int n = Integer.parseInt(sizeString);
    int d = Integer.parseInt($NUMERO.getText());
    addInstruccion(new Instruccion(10, n, d, 0));
    } }
  | POP NUMERO
  { if (phase == 1) {
    int d = Integer.parseInt($NUMERO.getText());
    addInstruccion(new Instruccion(10, 0, d, 0));
    } }
  | HALT
  { if (phase == 1) addInstruccion(new Instruccion(14, 0, 0, 0)); }
  ;

size returns [String s = ""]
  : OPEN_PAR (REGISTRO { s = $REGISTRO.getText(); } | NUMERO { s = $NUMERO.getText(); }) CLOSE_PAR
  ;
  
register returns [String s = ""]
  : OPEN_BRACKET REGISTRO { s = $REGISTRO.getText(); } CLOSE_BRACKET
  ;